昨天有講到 routes 可以與模型 繫結 Binding,讓 url 的參數在傳入 controller 時直接就找到 database 裡面的那一筆資料
但如果,使用者在網址輸入 www.example.com/prosuct/5
,試圖查看 id = 5 的產品,但這筆資料已經被 刪除 / 軟刪除 / 或是根本沒有這一筆資料呢?
Laravel 預設會直接給出 404 頁面,這個畫面沒有你的網站logo、沒有選單列、更沒有返回首頁。
真的只想取代這個 404頁面的話,官方文件也有提供取代方法。
若是純後端,則會像下面這樣報錯
這個訊息雖然不影響系統,但會有兩個問題:
Laravel 中的 "exception"(異常)是指在應用程序運行過程中可能引發的意外錯誤或異常情況。異常是一種特殊的事件,它可能會中斷正常的程式執行流程,並且通常需要特殊的處理來妥善處理錯誤情況。Laravel 使用異常處理來提供統一且清晰的方式來處理這些異常,以確保應用程序的穩定性和可讀性。
以下是有關 Laravel 中異常的一些重要概念:
ModelNotFoundException
(當試圖查找 model 時找不到數據)、ValidationException
(當輸入驗證失敗時引發)等。每種異常類型都代表不同的錯誤情況。App/Exceptions/Handler.php
文件中。這個處理程序允許您捕獲應用程序中引發的異常,然後根據異常的類型和上下文來執行特定的處理操作。這包括返回適當的 HTTP 響應、紀錄錯誤信息等。try...catch
塊來捕獲異常,以防止它們中斷代碼的執行。這使您可以在發生錯誤時進行適當的處理,而不會導致應用程序崩潰。Laravel 框架有完整的架構,要解決這個問題也可以從很多方法進行處理。(程式語言很多時候沒有 正解 ,需要依需求找尋最適合的解答)
當異常發生時,Laravel 預設會到 App/Exceptions/Handler.php 檔案找尋是否有定義錯誤處理方式,沒有的話就會以內建的預設處理方式進行 response。因此我們可以到 App/Exceptions/Handler.php 檔案中撰寫希望的錯誤處理方式
它就像是設定一個「全局錯誤應對計劃」,在應用程序啟動時,你告訴應用程序應該如何處理所有可能的錯誤情況。
reportable() 方法定義當 ModelNotFoundException 發生時應該執行什麼操作,如:記錄異常或通知到外部服務。
// App/Exceptions/Handler.php
use Illuminate\Database\Eloquent\ModelNotFoundException;
public function register()
{
$this->reportable(function (ModelNotFoundException $exception) {
// 在這裡執行報告異常的操作,例如將其記錄到日誌或通知到外部服務
});
}
renderable() 方法定義當 ModelNotFoundException 發生時應該返回什麼樣的 HTTP 響應,如:自訂錯誤頁面或 JSON 響應。
use Illuminate\Database\Eloquent\ModelNotFoundException;
public function register()
{
$this->renderable(function (ModelNotFoundException $exception, $request) {
// 在這裡定義當 ModelNotFoundException 發生時的響應,例如返回 404 頁面或 JSON 響應
return response()->json(['message' => '找不到相關資料'], 404);
});
}
//另一種寫法
public function register()
{
$this->renderable(function (NotFoundHttpException $e, $request) {
if ($request->is('api/product/*')) { // <- 如果來自被定義的 route
return response()->json([
'message' => '資料庫無此筆資料,請重新確認'
],
Response::HTTP_NOT_FOUND);
}
});
}
這個方法在底層邏輯是到了 controller , 發現找不到所屬 Model 資料時拋出異常
預設的 Laravel 專案可能沒有此方法,可以自行撰寫新增。
render() 方法可針對不同類型的錯誤客製化回覆,例如:ValidationException、AuthorizationException、FileNotFoundException、ModelNotFoundException……等。
public function render($request, Throwable $e)
{
if ($e instanceof **ModelNotFoundException**) { // 確切定義異常類型是哪一種
return response([
'message' => '無此資料,請重新確認'
]);
}
return parent::render($request, $e);
}
這個方法在 route 時就會查找是否有這筆資料,並用 abort_if() 回傳客製化訊息,不會產生**ModelNotFoundException
**,也不會進到 controller,對系統負擔更小。
這個方法可針對每個 model 設定不一樣的提示訊息,如:無此產品、無此圖片…等。
public function resolveRouteBinding($value, $field = null) // $field 是拿去搜尋 table 裡面的哪個欄位,預設為 id
{
abort_if(
!$this->where('id', $value)->first(),
Response::HTTP_BAD_REQUEST,
__("無此產品,請重新確認")
);
return $this->where('id', $value)->first();
}
// 如果要用 table 的其他欄位查找
public function resolveRouteBinding($value, $field = 'tag')
{
// 使用 $field 欄位的值來比對路由參數
return $this->where($field, $value)->first();
}
可以在定義 Route 時一併定義 missing
方法來自訂這個行為。missing
方法接受一個閉包,該閉包會在找不到隱式繫結的 Model 時被叫用:
use App\Http\Controllers\LocationsController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redirect;
Route::get('/product/{product:slug}', [ProductController::class, 'show'])
->missing(function (Request $request) {
return Redirect::route('/product'); // 重導向到別的 route
});